﻿using System;
using System.Collections.Generic;
using Gdi = System.Drawing;
using Gdi2D = System.Drawing.Drawing2D;
using GdiImaging = System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 影像大小調整工作。
	/// </summary>
	public class ResizeFilterTask : ImageFilterTask
	{
		#region 欄位
		BitmapInterpolationMode? _InterpolationMode;
		int _TargetPixelHeight = 128;
		int _TargetPixelWidth = 128;
        #endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 ResizeFilterTask 執行個體。
		/// </summary>
		public ResizeFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 ResizeFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceImage">來源點陣圖。</param>
		public ResizeFilterTask(IBitmapImage sourceImage)
			: base(sourceImage)
		{ }
		/// <summary>
		/// 初始化新的 ResizeFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceImage">來源點陣圖。</param>
		/// <param name="targetWidth">調整後的像素寬度。</param>
		/// <param name="targetHeight">調整後的像素高度。</param>
		/// <exception cref="ArgumentOutOfRangeException">targetWidth 或 targetHeight 不是正整數。</exception>
		public ResizeFilterTask(IBitmapImage sourceImage, int targetWidth, int targetHeight)
			: base(sourceImage)
		{
			if (targetWidth <= 0 || targetHeight <= 0)
				throw new ArgumentOutOfRangeException();
		}
        #endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="sourceImage">欲套用濾鏡的影像。</param>
		/// <param name="resultImage">已套用濾鏡的影像。</param>
		/// <returns>True 表示濾鏡套用成功，False 表示失敗。</returns>
		protected override bool ApplyFilter(Tasks.TaskVersion version, IImage sourceImage, out IImage resultImage)
		{
			//初始化參數
			resultImage = null;

			//取得資料
			int targetWidth, targetHeight;
			BitmapInterpolationMode? interpolationMode;
			if (!this.ReadValue<int>(version, ref _TargetPixelWidth, out targetWidth)
				|| !this.ReadValue<int>(version, ref _TargetPixelHeight, out targetHeight)
				|| !this.ReadValue<BitmapInterpolationMode?>(version, ref _InterpolationMode, out interpolationMode))
			{
				return true;
			}

			//取得來源 BitmapSource
			BitmapSource srcBitmapSource = sourceImage.ToImageSource().GetAsBitmapSource();

			//轉換來源像素格式
			PixelFormat pixelFormat = srcBitmapSource.Format;
			if (pixelFormat != PixelFormats.Bgr24
				&& pixelFormat != PixelFormats.Bgr32
				&& pixelFormat != PixelFormats.Bgra32
				&& pixelFormat != PixelFormats.Rgb48
				&& pixelFormat != PixelFormats.Rgba64)
			{
				pixelFormat = PixelFormats.Bgra32;
				srcBitmapSource = new FormatConvertedBitmap(srcBitmapSource, pixelFormat, null, 0.5);
			}

			//自動決定插補模式
			if (interpolationMode == null)
			{
				double sizeRatio = ((targetWidth * targetHeight) / (double)(srcBitmapSource.PixelWidth * srcBitmapSource.PixelHeight));
				if (sizeRatio <= 0.25)
					interpolationMode = BitmapInterpolationMode.HighQualityBilinear;
				else if (sizeRatio <= 0.5)
					interpolationMode = BitmapInterpolationMode.Bicubic;
				else if (sizeRatio < 1)
					interpolationMode = BitmapInterpolationMode.Bilinear;
				else
					interpolationMode = BitmapInterpolationMode.HighQualityBicubic;
			}

			//取得對應的像素格式
			GdiImaging.PixelFormat gdiPixelFormat;
			if (pixelFormat == PixelFormats.Bgr24)
				gdiPixelFormat = GdiImaging.PixelFormat.Format24bppRgb;
			else if (pixelFormat == PixelFormats.Bgr32)
				gdiPixelFormat = GdiImaging.PixelFormat.Format32bppRgb;
			else if (pixelFormat == PixelFormats.Bgra32)
				gdiPixelFormat = GdiImaging.PixelFormat.Format32bppArgb;
			else if (pixelFormat == PixelFormats.Rgb48)
				gdiPixelFormat = GdiImaging.PixelFormat.Format48bppRgb;
			else if (pixelFormat == PixelFormats.Rgba64)
				gdiPixelFormat = GdiImaging.PixelFormat.Format64bppArgb;
			else
				return false;

			//縮放點陣圖
			GCHandle? srcPixelBufferHandle = null;
			Gdi.Bitmap gdiSrcBitmap = null;
			Gdi.Bitmap gdiResultBitmap = null;
			try
			{
				//將來源轉成 GDI 點陣圖
				int stride = (pixelFormat.BitsPerPixel * srcBitmapSource.PixelWidth / 8);
				int extraBytes = (stride % 4);
				if (extraBytes > 0)
					stride += (4 - extraBytes);
				byte[] srcPixelBuffer = new byte[stride * srcBitmapSource.PixelHeight];
				srcPixelBufferHandle = GCHandle.Alloc(srcPixelBuffer, GCHandleType.Pinned);
				srcBitmapSource.CopyPixels(srcPixelBuffer, stride, 0);
				gdiSrcBitmap = new Gdi.Bitmap(srcBitmapSource.PixelWidth, srcBitmapSource.PixelHeight, stride, gdiPixelFormat, srcPixelBufferHandle.Value.AddrOfPinnedObject());

				//建立目標 GDI 點陣圖
				gdiResultBitmap = new Gdi.Bitmap(targetWidth, targetHeight, gdiPixelFormat);

				//中斷確認
				if (this.IsCanceling)
					return true;

				//縮放點陣圖
				using (Gdi.Graphics graphics = Gdi.Graphics.FromImage(gdiResultBitmap))
				{
					Gdi2D.InterpolationMode gdiInterpolationMode;
					switch (interpolationMode)
					{
						case BitmapInterpolationMode.NearestNeighbor:
							gdiInterpolationMode = Gdi2D.InterpolationMode.NearestNeighbor;
							break;
						case BitmapInterpolationMode.Bilinear:
							gdiInterpolationMode = Gdi2D.InterpolationMode.Bilinear;
							break;
						case BitmapInterpolationMode.Bicubic:
							gdiInterpolationMode = Gdi2D.InterpolationMode.Bicubic;
							break;
						case BitmapInterpolationMode.HighQualityBilinear:
							gdiInterpolationMode = Gdi2D.InterpolationMode.HighQualityBilinear;
							break;
						case BitmapInterpolationMode.HighQualityBicubic:
							gdiInterpolationMode = Gdi2D.InterpolationMode.HighQualityBicubic;
							break;
						default:
							return false;
					}
					graphics.InterpolationMode = gdiInterpolationMode;
					graphics.DrawImage(gdiSrcBitmap, new Gdi.Rectangle(0, 0, targetWidth, targetHeight), new Gdi.Rectangle(0, 0, gdiSrcBitmap.Width, gdiSrcBitmap.Height), Gdi.GraphicsUnit.Pixel);
				}

				//中斷確認
				if (this.IsCanceling)
					return true;

				//轉成 IBitmapImage
				GdiImaging.BitmapData gdiBitmapData = gdiResultBitmap.LockBits(new Gdi.Rectangle(0, 0, targetWidth, targetHeight), GdiImaging.ImageLockMode.ReadOnly, gdiPixelFormat);
				try
				{
					byte[] resultPixelBuffer = new byte[gdiBitmapData.Stride * targetHeight];
					Marshal.Copy(gdiBitmapData.Scan0, resultPixelBuffer, 0, resultPixelBuffer.Length);
					BitmapSource resultBitmapSource = BitmapSource.Create(targetWidth, targetHeight, srcBitmapSource.DpiX, srcBitmapSource.DpiY, pixelFormat, null, resultPixelBuffer, gdiBitmapData.Stride);
					resultImage = new BitmapImage(sourceImage.MediaStorage, resultBitmapSource, true, sourceImage.Metadata, sourceImage.RawFormat);
				}
				finally
				{
					gdiResultBitmap.UnlockBits(gdiBitmapData);
				}
			}
			finally
			{
				//釋放資源
				if (gdiSrcBitmap != null)
					gdiSrcBitmap.Dispose();
				if (gdiResultBitmap != null)
					gdiResultBitmap.Dispose();
				if (srcPixelBufferHandle != null)
					srcPixelBufferHandle.Value.Free();
			}

			//完成
			return true;
		}
        #endregion
		#region InterpolationMode
		/// <summary>
		/// 取得或設定插補模式，Null (Visual Basic 為 Nothing) 表示自動根據調整的大小決定，預設值為 Null (Visual Basic 為 Nothing)。
		/// </summary>
		public BitmapInterpolationMode? InterpolationMode
		{
			get
			{
				return _InterpolationMode;
			}
			set
			{
				if (value != null && !value.Value.IsDefined())
					throw new ArgumentException();
				this.SetInitializingParameter<BitmapInterpolationMode?>(ref _InterpolationMode, value);
			}
		}
		#endregion
		#region TargetPixelHeight
		/// <summary>
		/// 取得或設定調整後的像素高度，預設值為 128。
		/// </summary>
		public int TargetPixelHeight
		{
			get
			{
				return _TargetPixelHeight;
			}
			set
			{
				if (value <= 0)
					throw new ArgumentOutOfRangeException();
				this.SetInitializingParameter<int>(ref _TargetPixelHeight, value);
			}
		}
        #endregion
		#region TargetPixelWidth
		/// <summary>
		/// 取得或設定調整後的像素寬度，預設值為 128。
		/// </summary>
		public int TargetPixelWidth
		{
			get
			{
				return _TargetPixelWidth;
			}
			set
			{
				if (value <= 0)
					throw new ArgumentOutOfRangeException();
				this.SetInitializingParameter<int>(ref _TargetPixelWidth, value);
			}
		}
		#endregion
		#region ValidateInitParameters
		/// <summary>
		/// 驗證初始化參數是否正確。
		/// </summary>
		/// <returns>True 表示參數正確，False 表示錯誤。</returns>
		protected override bool ValidateInitParameters()
		{
			return  (base.ValidateInitParameters() && this.SourceImage is IBitmapImage);
		}
        #endregion
	}
	#region BitmapInterpolationMode
	/// <summary>
	/// 表示點陣圖插補方法。
	/// </summary>
	public enum BitmapInterpolationMode
	{
		/// <summary>
		/// 雙線形的插補法。 不做預先篩選。 這個模式不適用於將影像縮小到原始尺寸的百分之 50 以下。
		/// </summary>
		NearestNeighbor,
		/// <summary>
		/// 雙立方的插補法。 不做預先篩選。 這個模式不適用於將影像縮小到原始尺寸的百分之 25 以下。
		/// </summary>
		Bilinear,
		/// <summary>
		/// 最相鄰的插補法。
		/// </summary>
		Bicubic,
		/// <summary>
		/// 高品質的雙線形插補法。 將執行預先篩選，以確保縮小影像的品質。
		/// </summary>
		HighQualityBilinear,
		/// <summary>
		/// 高品質的雙立方插補法。 將執行預先篩選，以確保縮小影像的品質。 這個模式會產生品質最高的變形影像。
		/// </summary>
		HighQualityBicubic,
	}
	#endregion
}
